package com.main.beatit;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.regex.Pattern;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.media.AudioManager;
import android.os.*;
import android.preference.PreferenceManager;
import android.view.*;
import android.view.View.OnTouchListener;
import android.widget.*;

@SuppressLint("HandlerLeak")
public class MainActivity extends Activity {
	public static SoundManager mSoundManager; // To play sounds
	private boolean bPlayState; // Is play patterns?
	private boolean bMetronome; // Is play a metronome?
	public static float fBPM; // BPM information

	Display display; // Device display information
	int displayWidth;
	int displayHeight;

	PatternHandler hdlPattern;
	ButtonHandler hdlButton;
	static final int CHANGE_TO_NORMAL = 0;
	static final int CHANGE_TO_PUSH = 1;
	static final int CHANGE_PROG_BAR = 2;

	// Preference and Pattern activity
	private Intent iPref;
	private Intent iPattern;

	Thread thPlayPattern; // Thread about play sounds
	Handler mHandler;
	boolean bEnd; // Is Application Ended?
	boolean bStarted = false; // Is Application Started?
	boolean bRecord; // Is record mode?

	// About playing pattern index, beat(step) index, total enabled patterns
	public static int nPatternIndex, nBeatIndex, nTotalPattern;

	public static int[][] nPatterns; // Is that! Real pattern

	ImageView ivPads[]; // For sound image
	public static final int nSoundID = 1000;

	public static int nBeat = 1000 / 15; // 15 is BPM / 60 * 4 -> Calculate a
											// BPM

	public static AudioManager amVolume;
	
	// To calculate pad index
	public static final int[] DIGITS = { 128, // 1000 0000
			64, // 0100 0000
			32, // 0010 0000
			16, // 0001 0000
			8, // 0000 1000
			4, // 0000 0100
			2, // 0000 0010
			1, // 0000 0001
	};

	// Pad ID
	static final int[] PADS = { R.id.btn1, R.id.btn2, R.id.btn3, R.id.btn4, R.id.btn5, R.id.btn6, R.id.btn7, R.id.btn8 };

	static final int[] IMGPADS = { R.id.ivPad1, R.id.ivPad2, R.id.ivPad3, R.id.ivPad4, R.id.ivPad5, R.id.ivPad6,
			R.id.ivPad7, R.id.ivPad8 };

	public static int[] SOUNDIDS = { nSoundID, nSoundID + 1, nSoundID + 2, nSoundID + 3, nSoundID + 4, nSoundID + 5,
			nSoundID + 6, nSoundID + 7 };

	public static final int[] IMAGEIDS = { R.drawable.imgcrash, R.drawable.imghatcl, R.drawable.imghatop,
			R.drawable.imghatpd, R.drawable.imgkick, R.drawable.imgsnare, R.drawable.imgclap, R.drawable.imgcowbell,
			R.drawable.imgstick, R.drawable.imgtomfloor, R.drawable.imgtomlrg, R.drawable.imgride,
			R.drawable.imgshaker, R.drawable.imgtamb, R.drawable.imgvibra, R.drawable.imgspeak2 };

	// Menu button ID
	static final int[] BUTTONS = { R.id.btnPlay, R.id.btnStop, R.id.btnRecord, R.id.btnPattern };

	// Enabled patterns information
	public static boolean[] bEnablePatterns = { true, true, true, true, false, false, false, false, false, false,
			false, false, false, false, false, false };

	@Override
	public void onCreate(Bundle savedInstanceState) {
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// I don't use auto sleep mode.
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

		// For intro activity

		if (bStarted == false) {
			startActivity(new Intent(this, Intro.class));

			bStarted = true;
		}
		
		amVolume = (AudioManager)getSystemService(AUDIO_SERVICE);

		// Make preference and pattern view activity
		iPref = new Intent(this, Preference.class);
		iPattern = new Intent(this, Patterns.class);

		// Initialize soundpool
		mSoundManager = new SoundManager();
		mSoundManager.initSounds(getBaseContext());
		mSoundManager.addSound(nSoundID, R.raw.crash);
		mSoundManager.addSound(nSoundID + 1, R.raw.hatcl);
		mSoundManager.addSound(nSoundID + 2, R.raw.hatop);
		mSoundManager.addSound(nSoundID + 3, R.raw.hatpd);
		mSoundManager.addSound(nSoundID + 4, R.raw.kick);
		mSoundManager.addSound(nSoundID + 5, R.raw.snare);
		mSoundManager.addSound(nSoundID + 6, R.raw.clap);
		mSoundManager.addSound(nSoundID + 7, R.raw.cowbell);
		mSoundManager.addSound(nSoundID + 8, R.raw.stick);
		mSoundManager.addSound(nSoundID + 9, R.raw.tomfloor);
		mSoundManager.addSound(nSoundID + 10, R.raw.tomtoms);
		mSoundManager.addSound(nSoundID + 11, R.raw.ride);
		mSoundManager.addSound(nSoundID + 12, R.raw.shaker);
		mSoundManager.addSound(nSoundID + 13, R.raw.tamb);
		mSoundManager.addSound(nSoundID + 14, R.raw.vibra);
		mSoundManager.addSound(nSoundID + 15, R.raw.hitbrass);
		mSoundManager.addSound(1, R.raw.met_main);
		mSoundManager.addSound(2, R.raw.met_sub);

		// Initialize member variable
		bPlayState = false;
		fBPM = 94;
		nBeat = (int) (1000 / (fBPM / 15));
		bEnd = false;
		bRecord = false;
		nPatternIndex = 0;
		nBeatIndex = 0;
		nTotalPattern = 4;

		nPatterns = new int[16][16];

		for (int i = 0; i < 16; i++) {
			for (int j = 0; j < 16; j++) {
				nPatterns[i][j] = 0;
			}
		}

		// Get a display information.
		display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();

		displayWidth = display.getWidth();
		displayHeight = display.getHeight();

		hdlButton = new ButtonHandler();
		hdlPattern = new PatternHandler();

		// Is very important thread. Because, it play sounds
		thPlayPattern = new Thread(new Runnable() {
			public void run() {
				while (true) {
					// Is playing?
					if (bPlayState == true) {
						// For total enabled patterns
						int nNewTotalPattern = 0;

						// About all patterns
						for (nPatternIndex = 0; nPatternIndex < 16; nPatternIndex++) {

							// If this pattern is disabled, check next pattern.
							if (bEnablePatterns[nPatternIndex] == false) {
								continue;
							}

							nNewTotalPattern++;

							// About one pattern all steps
							for (nBeatIndex = 0; nBeatIndex < 16; nBeatIndex++) {
								// For play at BPM, calculate time.
								long lastTime = System.currentTimeMillis();

								if (bPlayState == false) {
									break;
								}

								// If you want to play metronome, play!
								if (bMetronome == true) {
									if (nBeatIndex == 0) {
										mSoundManager.playSound(1);
									} else if (nBeatIndex % 4 == 0) {
										mSoundManager.playSound(2);
									}
								}

								// Play sounds and draw pushed pad.
								for (int i = 0; i < 8; i++) {
									if ((nPatterns[nPatternIndex][nBeatIndex] & DIGITS[i]) == DIGITS[i]) {
										mSoundManager.playSound(SOUNDIDS[i]);
										hdlButton.sendMessage(hdlButton.obtainMessage(CHANGE_TO_PUSH,
												findViewById(PADS[i])));
									}
								}
								// Wait for time between step to step
								while (true) {
									long currentTime = System.currentTimeMillis();

									if (currentTime - lastTime >= nBeat) {
										// Release pads and go to next step.
										for (int i = 0; i < 8; i++) {
											hdlButton.sendMessage(hdlButton.obtainMessage(CHANGE_TO_NORMAL,
													findViewById(PADS[i])));
										}

										if (Patterns.bDraw == true) {
											if (Patterns.nNowPattern == nPatternIndex) {
												hdlPattern.sendMessage(hdlPattern.obtainMessage(CHANGE_PROG_BAR,
														nBeatIndex));
											} else {
												hdlPattern.sendMessage(hdlPattern.obtainMessage());
											}
										}

										break;
									} // if (currentTime - lastTime >= nBeat)
								} // while(true) -> wait for 'nBeat'

							} // for(nBeatIndex)

						} // for(nPatternIndex)

						nTotalPattern = nNewTotalPattern;
					} // if(bPlayState)
					else {
						nPatternIndex = 0;
					}
				} // while(true)
			} // run()
		});

		thPlayPattern.start();

		// test Sample Beat
		nPatterns[0][0] = 8 + 128;
		nPatterns[0][2] = 16;
		nPatterns[0][3] = 8;
		nPatterns[0][4] = 16 + 4;
		nPatterns[0][6] = 16;
		nPatterns[0][8] = 16;
		nPatterns[0][9] = 4;
		nPatterns[0][10] = 16;
		nPatterns[0][11] = 8;
		nPatterns[0][12] = 16 + 4;
		nPatterns[0][13] = 8;
		nPatterns[0][14] = 16;
		
		nPatterns[1][0] = 8 + 128;
		nPatterns[1][2] = 16;
		nPatterns[1][3] = 8;
		nPatterns[1][4] = 16 + 4;
		nPatterns[1][6] = 16;
		nPatterns[1][8] = 16;
		nPatterns[1][9] = 4;
		nPatterns[1][10] = 16;
		nPatterns[1][11] = 8;
		nPatterns[1][12] = 16 + 4;
		nPatterns[1][13] = 8;
		nPatterns[1][14] = 16;

		nPatterns[2][0] = 8 + 128;
		nPatterns[2][2] = 16;
		nPatterns[2][3] = 8;
		nPatterns[2][4] = 16 + 4;
		nPatterns[2][6] = 16;
		nPatterns[2][8] = 16;
		nPatterns[2][9] = 4;
		nPatterns[2][10] = 16;
		nPatterns[2][11] = 8;
		nPatterns[2][12] = 16 + 4;
		nPatterns[2][13] = 8;
		nPatterns[2][14] = 16;

		nPatterns[3][0] = 8 + 128;
		nPatterns[3][2] = 16;
		nPatterns[3][3] = 8;
		nPatterns[3][4] = 16 + 4;
		nPatterns[3][6] = 16;
		nPatterns[3][8] = 16;
		nPatterns[3][9] = 4;
		nPatterns[3][10] = 16;
		nPatterns[3][11] = 8;
		nPatterns[3][12] = 16 + 4;
		nPatterns[3][13] = 8;
		nPatterns[3][14] = 128;
		
		extractDefaultPatterns("Sample(Muse-Hysteria)", (int)fBPM);
		
		resetPatterns();
		
		nTotalPattern = 1;
		for(int i=1; i<4; i++)
		{
			bEnablePatterns[i] = false;
		}

		// For finish this application.
		mHandler = new Handler() {
			public void handleMessage(Message msg) {
				if (msg.what == 0) {
					bEnd = false;
				}
			}
		};

		// Initialize pads
		for (int btnId : PADS) {
			ImageView iv = (ImageView) findViewById(btnId);
			iv.setOnTouchListener(new OnTouchListener() {
				public boolean onTouch(View v, MotionEvent event) {
					// If pads touched, play sounds.
					if (event.getAction() == MotionEvent.ACTION_DOWN) {
						mSoundManager.playSound(SOUNDIDS[((v.getId() - R.id.btn1) / 2)]);
						v.setBackgroundResource(R.drawable.pad_push);

						if (bRecord) {
							if ((nPatterns[nPatternIndex][nBeatIndex] & DIGITS[(v.getId() - R.id.btn1) / 2]) == 0) {
								nPatterns[nPatternIndex][nBeatIndex] += DIGITS[(v.getId() - R.id.btn1) / 2];
							}
						}
					} else if (event.getAction() == MotionEvent.ACTION_UP) {
						v.setBackgroundResource(R.drawable.pad_normal);
					}

					return false;
				}
			});
		}

		// Initialize pad images
		ivPads = new ImageView[8];

		for (int i = 0; i < 8; i++) {
			ivPads[i] = (ImageView) findViewById(IMGPADS[i]);

			RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ivPads[i].getLayoutParams();
			params.width = (int) (displayWidth / 7);
			params.height = (int) (displayWidth / 7);
			params.leftMargin = displayWidth / 10 - params.width / 2;
			params.topMargin = displayHeight / 4 - params.width / 2;
			ivPads[i].setLayoutParams(params);
		}

		// Initialize menu buttons
		for (int btnId : BUTTONS) {
			ImageView iv = (ImageView) findViewById(btnId);
			iv.setOnTouchListener(new OnTouchListener() {
				public boolean onTouch(View v, MotionEvent event) {

					switch (v.getId()) {
						case R.id.btnPlay:
							if (event.getAction() == MotionEvent.ACTION_DOWN) {
								// Play patterns
								v.setBackgroundResource(R.drawable.play_push);
								findViewById(R.id.btnStop).setBackgroundResource(R.drawable.stop);
								bPlayState = true;
							}
							break;
						case R.id.btnStop:
							if (event.getAction() == MotionEvent.ACTION_DOWN) {
								// Stop to play patterns
								v.setBackgroundResource(R.drawable.stop_push);
								findViewById(R.id.btnPlay).setBackgroundResource(R.drawable.play);
								bPlayState = false;
							}
							break;

						case R.id.btnRecord:
							// Record mode
							if (bRecord == false) {
								if (event.getAction() == MotionEvent.ACTION_DOWN) {
									v.setBackgroundResource(R.drawable.record_push);
									bRecord = true;
								}
							} else {
								if (event.getAction() == MotionEvent.ACTION_DOWN) {
									v.setBackgroundResource(R.drawable.record);
									bRecord = false;
								}
							}
							break;
						case R.id.btnPattern:
							// Clear all patterns
							if (event.getAction() == MotionEvent.ACTION_DOWN) {
								v.setBackgroundResource(R.drawable.pattern_push);
							} else if (event.getAction() == MotionEvent.ACTION_UP) {
								v.setBackgroundResource(R.drawable.pattern);
								Patterns.bDraw = true;
								startActivity(iPattern);
							}
							break;

					}

					return false;
				}
			});
		}

	}

	@Override
	protected void onResume() {
		super.onResume();

		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);

		// set a metronome
		if (pref.getBoolean("metronome", false)) {
			bMetronome = true;
		} else {
			bMetronome = false;
		}

		for (int i = 0; i < 8; i++) {
			ivPads[i].setBackgroundResource(IMAGEIDS[SOUNDIDS[i] - nSoundID]);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);

		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		if (item.getItemId() == R.id.menu_preference) {
			startActivity(iPref);
		}

		return super.onOptionsItemSelected(item);
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			if (bEnd == false) {
				showToast("Do you want to close this app? One more 'Back'");

				bEnd = true;
				mHandler.sendEmptyMessageDelayed(0, 2000);

				return false;
			} else {
				finish();
			}
		}
		else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP)
		{
			amVolume.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
			
			return true;
		}
		else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
		{
			amVolume.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
			
			return true;
		}

		return super.onKeyDown(keyCode, event);
	}

	@Override
	public void finish() {
		android.os.Process.killProcess(android.os.Process.myPid());

		super.finish();
	}

	@Override
	protected void onDestroy() {
		bPlayState = false;

		super.onDestroy();
	}

	public void resetPatterns() {
		for (int i = 0; i < 16; i++) {
			for (int j = 0; j < 16; j++) {
				nPatterns[i][j] = 0;
			}
		}
	}

	public void showToast(String s) {
		Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
	}

	class ButtonHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
				case CHANGE_TO_NORMAL:
					((View) msg.obj).setBackgroundResource(R.drawable.pad_normal);
					break;
				case CHANGE_TO_PUSH:
					((View) msg.obj).setBackgroundResource(R.drawable.pad_push);
					break;
			}

			super.handleMessage(msg);
		}
	}

	class PatternHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			Patterns.drawPatterns(Patterns.nNowPattern);

			switch (msg.what) {
				case CHANGE_PROG_BAR:
					for (int i = 0; i < 8; i++) {
						Patterns.llPads[i][Integer.parseInt(msg.obj.toString())].setBackgroundColor(Color.argb(150,
								255, 255, 255));
					}

					break;

			}

			super.handleMessage(msg);
		}
	}
	
	public void extractDefaultPatterns(String sFileName, int nBPM) {
		try {
			String sBPM = "";
			String sPatterns = "";
			String sEnabled = "";

			if (nBPM / 100 == 0) {
				sBPM = String.valueOf(0);
			} else {
				sBPM = String.valueOf(nBPM / 100);
			}
			nBPM %= 100;
			if (nBPM / 10 == 0) {
				sBPM += String.valueOf(0);
			} else {
				sBPM += String.valueOf(nBPM / 10);
			}
			nBPM %= 10;
			if (nBPM == 0) {
				sBPM += String.valueOf(0);
			} else {
				sBPM += String.valueOf(nBPM);
			}

			for (int i = 0; i < 16; i++) {
				if (MainActivity.bEnablePatterns[i] == true) {
					sEnabled += "o";
				} else {
					sEnabled += "x";
				}
			}

			for (int i = 0; i < 16; i++) {
				for (int j = 0; j < 16; j++) {
					sPatterns += String.valueOf(MainActivity.nPatterns[i][j]);

					if (j == 15) {
						sPatterns += ";";
					} else {
						sPatterns += ",";
					}
				}
			}

			FileOutputStream fo = openFileOutput(sFileName + ".bti", Context.MODE_WORLD_READABLE);
			fo.write("beatit".getBytes());
			fo.write(("_" + sBPM).getBytes());
			fo.write(sEnabled.getBytes());
			fo.write(sPatterns.getBytes());
			fo.close();
		} catch (Exception e) {

			showToast("Extract failed...");
		}

	}
}